package com.structurizr.dsl;

import com.structurizr.Workspace;
import com.structurizr.model.*;
import com.structurizr.view.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

class DslTests extends AbstractTests {

    @Test
    void test_test() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/test.dsl"));

        assertFalse(parser.getWorkspace().isEmpty());
    }

    @Test
    void test_utf8() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/utf8.dsl"));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();

        assertEquals(1, model.getPeople().size());
        Person user = model.getPersonWithName("你好 Usér \uD83D\uDE42");
        assertNotNull(user);
    }

    @Test
    void test_gettingstarted() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/getting-started.dsl"));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();
        ViewSet views = workspace.getViews();

        assertEquals(1, model.getPeople().size());
        Person user = model.getPersonWithName("User");

        assertEquals(1, workspace.getModel().getSoftwareSystems().size());
        SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System");

        assertEquals(1, workspace.getModel().getRelationships().size());
        Relationship relationship = user.getRelationships().iterator().next();
        assertEquals("Uses", relationship.getDescription());
        assertSame(softwareSystem, relationship.getDestination());

        assertEquals(1, views.getViews().size());
        assertEquals(1, views.getSystemContextViews().size());
        SystemContextView view = views.getSystemContextViews().iterator().next();
        assertEquals("SystemContext-001", view.getKey());
        assertEquals(2, view.getElements().size());
        assertEquals(1, view.getRelationships().size());
    }

    @Test
    void test_aws() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/amazon-web-services.dsl"));

        Workspace workspace = parser.getWorkspace();

        assertEquals(13, workspace.getModel().getElements().size());
        assertEquals(0, workspace.getModel().getPeople().size());
        assertEquals(1, workspace.getModel().getSoftwareSystems().size());
        assertEquals(2, workspace.getModel().getSoftwareSystemWithName("Spring PetClinic").getContainers().size());
        assertEquals(1, workspace.getModel().getDeploymentNodes().size());
        assertEquals(6, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count());
        assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count());
        assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count());

        assertEquals(4, workspace.getModel().getRelationships().size());

        assertEquals(0, workspace.getViews().getSystemLandscapeViews().size());
        assertEquals(0, workspace.getViews().getSystemContextViews().size());
        assertEquals(0, workspace.getViews().getContainerViews().size());
        assertEquals(0, workspace.getViews().getComponentViews().size());
        assertEquals(0, workspace.getViews().getDynamicViews().size());
        assertEquals(1, workspace.getViews().getDeploymentViews().size());

        DeploymentView deploymentView = workspace.getViews().getDeploymentViews().iterator().next();
        assertEquals(10, deploymentView.getElements().size());
        assertEquals(3, deploymentView.getRelationships().size());
        assertEquals(4, deploymentView.getAnimations().size());

        assertEquals(3, workspace.getViews().getConfiguration().getStyles().getElements().size());
        assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size());

        assertEquals(1, workspace.getViews().getConfiguration().getThemes().length);
    }

    @Test
    void test_awsLocal() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/amazon-web-services-local.dsl"));

        Workspace workspace = parser.getWorkspace();

        assertEquals(13, workspace.getModel().getElements().size());
        assertEquals(0, workspace.getModel().getPeople().size());
        assertEquals(1, workspace.getModel().getSoftwareSystems().size());
        assertEquals(2, workspace.getModel().getSoftwareSystemWithName("Spring PetClinic").getContainers().size());
        assertEquals(1, workspace.getModel().getDeploymentNodes().size());
        assertEquals(6, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count());
        assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count());
        assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count());

        assertEquals(4, workspace.getModel().getRelationships().size());

        assertEquals(0, workspace.getViews().getSystemLandscapeViews().size());
        assertEquals(0, workspace.getViews().getSystemContextViews().size());
        assertEquals(0, workspace.getViews().getContainerViews().size());
        assertEquals(0, workspace.getViews().getComponentViews().size());
        assertEquals(0, workspace.getViews().getDynamicViews().size());
        assertEquals(1, workspace.getViews().getDeploymentViews().size());

        DeploymentView deploymentView = workspace.getViews().getDeploymentViews().iterator().next();
        assertEquals(10, deploymentView.getElements().size());
        assertEquals(3, deploymentView.getRelationships().size());
        assertEquals(4, deploymentView.getAnimations().size());

        assertEquals(3, workspace.getViews().getConfiguration().getStyles().getElements().size());
        assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size());

        assertEquals(1, workspace.getViews().getConfiguration().getThemes().length);
    }

    @Test
    void test_bigbankplc() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/big-bank-plc.dsl"));

        Workspace workspace = parser.getWorkspace();

        assertEquals(51, workspace.getModel().getElements().size());
        assertEquals(3, workspace.getModel().getPeople().size());
        assertEquals(4, workspace.getModel().getSoftwareSystems().size());
        assertEquals(5, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getContainers().size());
        assertEquals(6, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getContainerWithName("API Application").getComponents().size());
        assertEquals(5, workspace.getModel().getDeploymentNodes().size());
        assertEquals(21, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count());
        assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).count());
        assertEquals(10, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count());
        assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count());

        assertEquals(42, workspace.getModel().getRelationships().size());

        assertEquals(1, workspace.getViews().getSystemLandscapeViews().size());
        assertEquals(1, workspace.getViews().getSystemContextViews().size());
        assertEquals(1, workspace.getViews().getContainerViews().size());
        assertEquals(1, workspace.getViews().getComponentViews().size());
        assertEquals(1, workspace.getViews().getDynamicViews().size());
        assertEquals(2, workspace.getViews().getDeploymentViews().size());

        assertEquals(7, workspace.getViews().getSystemLandscapeViews().iterator().next().getElements().size());
        assertEquals(9, workspace.getViews().getSystemLandscapeViews().iterator().next().getRelationships().size());

        assertEquals(4, workspace.getViews().getSystemContextViews().iterator().next().getElements().size());
        assertEquals(4, workspace.getViews().getSystemContextViews().iterator().next().getRelationships().size());

        assertEquals(8, workspace.getViews().getContainerViews().iterator().next().getElements().size());
        assertEquals(10, workspace.getViews().getContainerViews().iterator().next().getRelationships().size());

        assertEquals(11, workspace.getViews().getComponentViews().iterator().next().getElements().size());
        assertEquals(13, workspace.getViews().getComponentViews().iterator().next().getRelationships().size());

        assertEquals(4, workspace.getViews().getDynamicViews().iterator().next().getElements().size());
        assertEquals(6, workspace.getViews().getDynamicViews().iterator().next().getRelationships().size());

        assertEquals(13, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("DevelopmentDeployment")).findFirst().get().getElements().size());
        assertEquals(4, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("DevelopmentDeployment")).findFirst().get().getRelationships().size());

        assertEquals(20, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("LiveDeployment")).findFirst().get().getElements().size());
        assertEquals(7, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("LiveDeployment")).findFirst().get().getRelationships().size());

        assertEquals(11, workspace.getViews().getConfiguration().getStyles().getElements().size());
        assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size());

        assertEquals(0, workspace.getViews().getConfiguration().getThemes().length);

        assertEquals(0, workspace.getDocumentation().getSections().size());
        assertEquals(0, workspace.getDocumentation().getDecisions().size());
    }

    @Test
    void test_bigbankplc_systemlandscape() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/big-bank-plc/system-landscape.dsl"));

        Workspace workspace = parser.getWorkspace();

        assertEquals(7, workspace.getModel().getElements().size());
        assertEquals(3, workspace.getModel().getPeople().size());
        assertEquals(4, workspace.getModel().getSoftwareSystems().size());

        assertEquals(9, workspace.getModel().getRelationships().size());

        assertEquals(1, workspace.getViews().getSystemLandscapeViews().size());
        assertEquals(0, workspace.getViews().getSystemContextViews().size());
        assertEquals(0, workspace.getViews().getContainerViews().size());
        assertEquals(0, workspace.getViews().getComponentViews().size());
        assertEquals(0, workspace.getViews().getDynamicViews().size());
        assertEquals(0, workspace.getViews().getDeploymentViews().size());

        assertEquals(7, workspace.getViews().getSystemLandscapeViews().iterator().next().getElements().size());
        assertEquals(9, workspace.getViews().getSystemLandscapeViews().iterator().next().getRelationships().size());

        assertEquals(4, workspace.getViews().getConfiguration().getStyles().getElements().size());
        assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size());

        assertEquals(0, workspace.getViews().getConfiguration().getThemes().length);
    }

    @Test
    void test_bigbankplc_internetbankingsystem() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/big-bank-plc/internet-banking-system.dsl"));

        Workspace workspace = parser.getWorkspace();

        assertEquals(51, workspace.getModel().getElements().size());
        assertEquals(3, workspace.getModel().getPeople().size());
        assertEquals(4, workspace.getModel().getSoftwareSystems().size());
        assertEquals(5, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getContainers().size());
        assertEquals(6, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getContainerWithName("API Application").getComponents().size());
        assertEquals(5, workspace.getModel().getDeploymentNodes().size());
        assertEquals(21, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count());
        assertEquals(2, workspace.getModel().getElements().stream().filter(e -> e instanceof SoftwareSystemInstance).count());
        assertEquals(10, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count());
        assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count());

        assertEquals(42, workspace.getModel().getRelationships().size());

        assertEquals(0, workspace.getViews().getSystemLandscapeViews().size());
        assertEquals(1, workspace.getViews().getSystemContextViews().size());
        assertEquals(1, workspace.getViews().getContainerViews().size());
        assertEquals(1, workspace.getViews().getComponentViews().size());
        assertEquals(1, workspace.getViews().getDynamicViews().size());
        assertEquals(2, workspace.getViews().getDeploymentViews().size());

        assertEquals(4, workspace.getViews().getSystemContextViews().iterator().next().getElements().size());
        assertEquals(4, workspace.getViews().getSystemContextViews().iterator().next().getRelationships().size());

        assertEquals(8, workspace.getViews().getContainerViews().iterator().next().getElements().size());
        assertEquals(10, workspace.getViews().getContainerViews().iterator().next().getRelationships().size());

        assertEquals(11, workspace.getViews().getComponentViews().iterator().next().getElements().size());
        assertEquals(13, workspace.getViews().getComponentViews().iterator().next().getRelationships().size());

        assertEquals(4, workspace.getViews().getDynamicViews().iterator().next().getElements().size());
        assertEquals(6, workspace.getViews().getDynamicViews().iterator().next().getRelationships().size());

        assertEquals(13, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("DevelopmentDeployment")).findFirst().get().getElements().size());
        assertEquals(4, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("DevelopmentDeployment")).findFirst().get().getRelationships().size());

        assertEquals(20, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("LiveDeployment")).findFirst().get().getElements().size());
        assertEquals(7, workspace.getViews().getDeploymentViews().stream().filter(v -> v.getKey().equals("LiveDeployment")).findFirst().get().getRelationships().size());

        assertEquals(11, workspace.getViews().getConfiguration().getStyles().getElements().size());
        assertEquals(0, workspace.getViews().getConfiguration().getStyles().getRelationships().size());

        assertEquals(0, workspace.getViews().getConfiguration().getThemes().length);

        assertEquals(4, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getDocumentation().getSections().size());
        assertEquals(1, workspace.getModel().getSoftwareSystemWithName("Internet Banking System").getDocumentation().getDecisions().size());
    }

    @Test
    void test_frs() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/financial-risk-system.dsl"));


        Workspace workspace = parser.getWorkspace();

        assertEquals(9, workspace.getModel().getElements().size());
        assertEquals(2, workspace.getModel().getPeople().size());
        assertEquals(7, workspace.getModel().getSoftwareSystems().size());
        assertEquals(0, workspace.getModel().getDeploymentNodes().size());
        assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof DeploymentNode).count());
        assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof ContainerInstance).count());
        assertEquals(0, workspace.getModel().getElements().stream().filter(e -> e instanceof InfrastructureNode).count());

        assertEquals(9, workspace.getModel().getRelationships().size());

        assertEquals(0, workspace.getViews().getSystemLandscapeViews().size());
        assertEquals(1, workspace.getViews().getSystemContextViews().size());
        assertEquals(0, workspace.getViews().getContainerViews().size());
        assertEquals(0, workspace.getViews().getComponentViews().size());
        assertEquals(0, workspace.getViews().getDynamicViews().size());
        assertEquals(0, workspace.getViews().getDeploymentViews().size());

        assertEquals(9, workspace.getViews().getSystemContextViews().iterator().next().getElements().size());
        assertEquals(9, workspace.getViews().getSystemContextViews().iterator().next().getRelationships().size());

        assertEquals(5, workspace.getViews().getConfiguration().getStyles().getElements().size());
        assertEquals(4, workspace.getViews().getConfiguration().getStyles().getRelationships().size());

        assertEquals(0, workspace.getViews().getConfiguration().getThemes().length);
    }

    @Test
    void test_includeLocalFile() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/include-file.dsl"));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();

        assertEquals(1, model.getSoftwareSystems().size());
        assertNotNull(model.getSoftwareSystemWithName("Software System"));

        assertEquals("workspace {\n" +
                "\n" +
                "    model {\n" +
                "        softwareSystem = softwareSystem \"Software System\" {\n" +
                "            !docs docs\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
    }

    @Test
    void test_includeLocalDirectory() throws Exception {
        File hiddenFile = new File("src/test/resources/dsl/include/model/software-system/.DS_Store");
        if (hiddenFile.exists()) {
            hiddenFile.delete();
        }

        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/include-directory.dsl"));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();

        assertEquals(3, workspace.getModel().getSoftwareSystems().size());
        SoftwareSystem softwareSystem1 = model.getSoftwareSystemWithName("Software System 1");
        assertNotNull(softwareSystem1);
        assertEquals(1, softwareSystem1.getDocumentation().getSections().size());

        SoftwareSystem softwareSystem2 = model.getSoftwareSystemWithName("Software System 2");
        assertNotNull(softwareSystem2);
        assertEquals(1, softwareSystem2.getDocumentation().getSections().size());

        SoftwareSystem softwareSystem3 = model.getSoftwareSystemWithName("Software System 3");
        assertNotNull(softwareSystem3);
        assertEquals(1, softwareSystem3.getDocumentation().getSections().size());

        assertEquals("workspace {\n" +
                "\n" +
                "    model {\n" +
                "        !var SOFTWARE_SYSTEM_NAME \"Software System 1\"\n" +
                "        softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" +
                "            !docs ../../docs\n" +
                "        }\n" +
                "\n" +
                "        !var SOFTWARE_SYSTEM_NAME \"Software System 2\"\n" +
                "        softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" +
                "            !docs ../../docs\n" +
                "        }\n" +
                "\n" +
                "        !var SOFTWARE_SYSTEM_NAME \"Software System 3\"\n" +
                "        softwareSystem \"${SOFTWARE_SYSTEM_NAME}\" {\n" +
                "            !docs ../../docs\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
    }

    @Test
    void test_includeLocalDirectory_WhenThereAreHiddenFiles() throws Exception {
        File hiddenFile = new File("src/test/resources/dsl/include/model/software-system/.DS_Store");
        if (hiddenFile.exists()) {
            hiddenFile.delete();
        }
        Files.writeString(hiddenFile.toPath(), "hello world");

        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/include-directory.dsl"));
    }

    @Test
    void test_includeUrl() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/include-url.dsl"));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();
        ViewSet views = workspace.getViews();

        assertEquals(1, workspace.getModel().getSoftwareSystems().size());
        SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System");

        assertEquals("workspace {\n" +
                "\n" +
                "    model {\n" +
                "        softwareSystem = softwareSystem \"Software System\" {\n" +
                "            !docs docs\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
    }

    @Test
    void test_include_WhenRunningInRestrictedMode() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.setRestricted(true);

        // the model include will be ignored, so no software systems
        parser.parse(new File("src/test/resources/dsl/include-file.dsl"));
        assertEquals(0, model.getSoftwareSystems().size());
    }

    @ParameterizedTest
    @ValueSource(strings = { "src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl", "src/test/resources/dsl/extend/extend-workspace-from-json-url.dsl" })
    void test_extendWorkspaceFromJson(String dslFile) throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File(dslFile));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();
        assertEquals(CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.class, model.getImpliedRelationshipsStrategy().getClass());

        assertEquals(1, model.getPeople().size());
        Person user = model.getPersonWithName("User");

        assertEquals(3, workspace.getModel().getSoftwareSystems().size());
        SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System 1");
        assertTrue(user.hasEfferentRelationshipWith(softwareSystem, "Uses"));

        assertEquals(2, softwareSystem.getContainers().size());
        assertNotNull(softwareSystem.getContainers().stream().filter(c -> c.getName().equals("Web Application 1")).findFirst());
        assertNotNull(softwareSystem.getContainers().stream().filter(c -> c.getName().equals("Web Application 2")).findFirst());
    }

    @Test
    void test_extendWorkspaceFromJsonFile_WhenRunningInRestrictedMode() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.setRestricted(true);

        File dslFile = new File("src/test/resources/dsl/extend/extend-workspace-from-json-file.dsl");

        try {
            // this will fail, because the model import will be ignored
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Cannot import workspace from a file when running in restricted mode at line 1 of " + dslFile.getAbsolutePath() + ": workspace extends workspace.json {", e.getMessage());
        }
    }

    @ParameterizedTest
    @ValueSource(strings = { "src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl", "src/test/resources/dsl/extend/extend-workspace-from-dsl-url.dsl" })
    void test_extendWorkspaceFromDsl(String dslFile) throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File(dslFile));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();
        assertEquals(CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy.class, model.getImpliedRelationshipsStrategy().getClass());

        assertEquals(1, model.getPeople().size());
        Person user = model.getPersonWithName("User");

        assertEquals(3, workspace.getModel().getSoftwareSystems().size());
        SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System 1");
        assertTrue(user.hasEfferentRelationshipWith(softwareSystem, "Uses"));

        assertEquals(1, softwareSystem.getContainers().size());
        assertEquals("Web Application", softwareSystem.getContainers().iterator().next().getName());
    }

    @Test
    void test_extendWorkspaceFromDslFile_WhenRunningInRestrictedMode() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.setRestricted(true);

        File dslFile = new File("src/test/resources/dsl/extend/extend-workspace-from-dsl-file.dsl");
        try {
            // this will fail, because the model import will be ignored
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Cannot import workspace from a file when running in restricted mode at line 1 of " + dslFile.getAbsolutePath() +": workspace extends workspace.dsl {", e.getMessage());
        }
    }

    @Test
    void test_extendWorkspaceFromDslFiles() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/extend/4.dsl"));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();
        ViewSet views = workspace.getViews();

        assertEquals(3, model.getPeople().size());
        assertEquals(1, views.getViews().size());
    }

    @Test
    void test_ref() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/ref.dsl"));

        assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/New deployment node/New infrastructure node"));
        assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/US-East-1/New deployment node 1/New infrastructure node 1"));
        assertNotNull(parser.getWorkspace().getModel().getElementWithCanonicalName("InfrastructureNode://Live/Amazon Web Services/US-East-1/New deployment node 2/New infrastructure node 2"));
    }

    @Test
    void test_parallel1() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/parallel1.dsl"));

        assertFalse(parser.getWorkspace().isEmpty());
        DynamicView view = parser.getWorkspace().getViews().getDynamicViews().iterator().next();
        List<RelationshipView> relationships = new ArrayList<>(view.getRelationships());
        assertEquals(4, relationships.size());
        assertEquals("1", relationships.get(0).getOrder());
        assertEquals("2", relationships.get(1).getOrder());
        assertEquals("3", relationships.get(2).getOrder());
        assertEquals("3", relationships.get(3).getOrder());
    }

    @Test
    void test_parallel2() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/parallel2.dsl"));

        assertFalse(parser.getWorkspace().isEmpty());
        DynamicView view = parser.getWorkspace().getViews().getDynamicViews().iterator().next();
        List<RelationshipView> relationships = new ArrayList<>(view.getRelationships());
        assertEquals(4, relationships.size());
        assertEquals("1", relationships.get(0).getOrder());
        assertEquals("2", relationships.get(1).getOrder());
        assertEquals("2", relationships.get(2).getOrder());
        assertEquals("3", relationships.get(3).getOrder());
    }

    @Test
    void test_groups() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/groups.dsl"));

        ContainerView containerView = parser.getWorkspace().getViews().getContainerViews().iterator().next();
        assertEquals(4, containerView.getElements().size());

        DeploymentView deploymentView = parser.getWorkspace().getViews().getDeploymentViews().iterator().next();
        assertEquals(6, deploymentView.getElements().size());
    }

    @Test
    void test_nested_groups() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/groups-nested.dsl"));

        SoftwareSystem a = parser.getWorkspace().getModel().getSoftwareSystemWithName("A");
        assertEquals("Organisation/Department A", a.getGroup());

        Container aApi = a.getContainerWithName("A API");
        assertEquals("Capability 1/Service A", aApi.getGroup());

        Container aDatabase = a.getContainerWithName("A Database");
        assertEquals("Capability 1/Service A", aDatabase.getGroup());

        Container bApi = a.getContainerWithName("B API");
        assertEquals("Capability 1/Service B", bApi.getGroup());

        Container bDatabase = a.getContainerWithName("B Database");
        assertEquals("Capability 1/Service B", bDatabase.getGroup());

        SoftwareSystem b = parser.getWorkspace().getModel().getSoftwareSystemWithName("B");
        assertEquals("Organisation/Department B", b.getGroup());

        SoftwareSystem c = parser.getWorkspace().getModel().getSoftwareSystemWithName("C");
        assertEquals("Organisation", c.getGroup());
    }

    @Test
    void test_hierarchicalIdentifiers() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers.dsl"));

        Workspace workspace = parser.getWorkspace();
        assertEquals(0, workspace.getModel().getSoftwareSystemWithName("B").getRelationships().size());
    }

    @Test
    void test_hierarchicalIdentifiersWhenUnassigned() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-when-unassigned.dsl"));

        Workspace workspace = parser.getWorkspace();
        IdentifiersRegister identifiersRegister = parser.getIdentifiersRegister();

        assertEquals(6, identifiersRegister.getElementIdentifiers().size());
        for (String identifier : identifiersRegister.getElementIdentifiers()) {
            assertFalse(identifier.startsWith("null"));
        }
    }

    @Test
    void test_hierarchicalIdentifiersAndDeploymentNodes() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-1.dsl"));
    }

    @Test
    void test_hierarchicalIdentifiersAndDeploymentNodes_WhenSoftwareSystemNameClashes() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-2.dsl"));
    }

    @Test
    void test_hierarchicalIdentifiersAndDeploymentNodes_WhenSoftwareContainerClashes() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/hierarchical-identifiers-and-deployment-nodes-3.dsl"));
    }

    @Test
    void test_pluginWithoutParameters() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/plugin-without-parameters.dsl"));

        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Java"));
    }

    @Test
    void test_pluginWithParameters() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/plugin-with-parameters.dsl"));

        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Java"));
    }

    @Test
    void test_script() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/script-external.dsl"));

        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Groovy"));
        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Kotlin"));
        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Ruby"));
    }

    @Test
    void test_scriptWithParameters() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/script-external-with-parameters.dsl"));

        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Groovy"));
    }

    @Test
    void test_inlineScript() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/script-inline.dsl"));

        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Groovy"));
        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Kotlin"));
        assertNotNull(parser.getWorkspace().getModel().getPersonWithName("Ruby"));

        assertTrue(parser.getWorkspace().getModel().getPersonWithName("User").hasTag("Groovy"));
        assertTrue(parser.getWorkspace().getModel().getPersonWithName("User").getRelationships().iterator().next().hasTag("Groovy"));
        assertEquals("Groovy", parser.getWorkspace().getViews().getSystemLandscapeViews().iterator().next().getDescription());
    }

    @Test
    void test_docs() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/docs/workspace.dsl"));

        SoftwareSystem softwareSystem = parser.getWorkspace().getModel().getSoftwareSystemWithName("Software System");
        Container container = softwareSystem.getContainerWithName("Container");
        Component component = container.getComponentWithName("Component");

        assertEquals(1, parser.getWorkspace().getDocumentation().getSections().size());
        assertEquals(1, softwareSystem.getDocumentation().getSections().size());
        assertEquals(1, container.getDocumentation().getSections().size());
        assertEquals(1, component.getDocumentation().getSections().size());
    }

    @Test
    void test_decisions() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/decisions/workspace.dsl"));

        SoftwareSystem softwareSystem = parser.getWorkspace().getModel().getSoftwareSystemWithName("Software System");
        Container container = softwareSystem.getContainerWithName("Container");
        Component component = container.getComponentWithName("Component");

        // adrtools decisions
        assertEquals(10, parser.getWorkspace().getDocumentation().getDecisions().size());
        assertEquals(10, softwareSystem.getDocumentation().getDecisions().size());

        // madr decisions
        assertEquals(19, container.getDocumentation().getDecisions().size());

        // log4brains decisions
        assertEquals(4, component.getDocumentation().getDecisions().size());
    }

    @Test
    void test_this() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/this.dsl"));
    }

    @Test
    void test_workspaceWithControlCharacters() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/workspace-with-bom.dsl"));
    }

    @Test
    void test_excludeRelationships() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/exclude-relationships.dsl"));
    }

    @Test
    void test_unexpectedTokensBeforeWorkspace() {
        File dslFile = new File("src/test/resources/dsl/unexpected-tokens-before-workspace.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Unexpected tokens (expected: workspace) at line 1 of " + dslFile.getAbsolutePath() + ": hello world", e.getMessage());
        }
    }

    @Test
    void test_unexpectedTokensAfterWorkspace() {
        File dslFile = new File("src/test/resources/dsl/unexpected-tokens-after-workspace.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Unexpected tokens at line 4 of " + dslFile.getAbsolutePath() + ": hello world", e.getMessage());
        }
    }

    @Test
    void test_unexpectedTokensInWorkspace() {
        File dslFile = new File("src/test/resources/dsl/unexpected-tokens-in-workspace.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Unexpected tokens (expected: name, description, properties, !docs, !decisions, !identifiers, !impliedRelationships, model, views, configuration) at line 3 of " + dslFile.getAbsolutePath() + ": softwareSystem \"Name\"", e.getMessage());
        }
    }

    @Test
    void test_urlNotPermittedInGroup() {
        File dslFile = new File("src/test/resources/dsl/group-url.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Unexpected tokens (expected: !docs, !decisions, group, container, description, tags, url, properties, perspectives, ->) at line 6 of " + dslFile.getAbsolutePath() + ": url \"https://example.com\"", e.getMessage());
        }
    }

    @Test
    void test_multipleWorkspaceTokens_ThrowsAnException() {
        File dslFile = new File("src/test/resources/dsl/multiple-workspace-tokens.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Multiple workspaces are not permitted in a DSL definition at line 9 of " + dslFile.getAbsolutePath() + ": workspace {", e.getMessage());
        }
    }

    @Test
    void test_multipleModelTokens_ThrowsAnException() {
        File dslFile = new File("src/test/resources/dsl/multiple-model-tokens.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Multiple models are not permitted in a DSL definition at line 7 of " + dslFile.getAbsolutePath() + ": model {", e.getMessage());
        }
    }

    @Test
    void test_multipleViewTokens_ThrowsAnException() {
        File dslFile = new File("src/test/resources/dsl/multiple-view-tokens.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Multiple view sets are not permitted in a DSL definition at line 13 of " + dslFile.getAbsolutePath() + ": views {", e.getMessage());
        }
    }

    @Test
    void test_dynamicViewWithExplicitRelationships() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/dynamic-view-with-explicit-relationships.dsl"));
    }

    @Test
    void test_dynamicViewWithCustomElements() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/dynamic-view-with-custom-elements.dsl"));
    }

    @Test
    void test_workspaceProperties() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/workspace-properties.dsl"));

        assertEquals("false", parser.getWorkspace().getProperties().get("structurizr.dslEditor"));
    }

    @Test
    void test_viewsWithoutKeys() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/views-without-keys.dsl"));

        assertTrue(parser.getWorkspace().getViews().getSystemLandscapeViews().stream().anyMatch(view -> view.getKey().equals("SystemLandscape-001")));
        assertTrue(parser.getWorkspace().getViews().getSystemLandscapeViews().stream().anyMatch(view -> view.getKey().equals("SystemLandscape-002")));
        assertTrue(parser.getWorkspace().getViews().getSystemLandscapeViews().stream().anyMatch(view -> view.getKey().equals("SystemLandscape-003")));
    }

    @Test
    void test_identifiers() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/identifiers.dsl"));

        Workspace workspace = parser.getWorkspace();
        Model model = workspace.getModel();

        Person user = model.getPersonWithName("User");
        SoftwareSystem softwareSystem = model.getSoftwareSystemWithName("Software System");
        Container container = softwareSystem.getContainerWithName("Container");
        Relationship relationship = user.getEfferentRelationshipWith(container);
        Relationship impliedRelationship = user.getEfferentRelationshipWith(softwareSystem);

        IdentifiersRegister register = parser.getIdentifiersRegister();
        assertEquals("user", register.findIdentifier(user));
        assertEquals("softwaresystem", register.findIdentifier(softwareSystem));
        assertEquals("softwaresystem.container", register.findIdentifier(container));
        assertEquals("rel", register.findIdentifier(relationship));

        assertSame(user, register.getElement("user"));
        assertSame(softwareSystem, register.getElement("softwareSystem"));
        assertSame(container, register.getElement("softwareSystem.container"));
        assertSame(relationship, register.getRelationship("rel"));

        assertEquals("user", user.getProperties().get("structurizr.dsl.identifier"));
        assertEquals("softwaresystem", softwareSystem.getProperties().get("structurizr.dsl.identifier"));
        assertEquals("softwaresystem.container", container.getProperties().get("structurizr.dsl.identifier"));
        assertEquals("rel", relationship.getProperties().get("structurizr.dsl.identifier"));
        assertNull(impliedRelationship.getProperties().get("structurizr.dsl.identifier"));
    }

    @Test
    void test_imageViews_ViaFiles() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/image-views/workspace-via-file.dsl"));

        Workspace workspace = parser.getWorkspace();
        assertEquals(5, workspace.getViews().getImageViews().size());

        ImageView plantumlView = (ImageView)workspace.getViews().getViewWithKey("plantuml");
        assertEquals("diagram.puml", plantumlView.getTitle());
        assertEquals("http://localhost:7777/png/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", plantumlView.getContent());
        assertEquals("image/png", plantumlView.getContentType());

        ImageView mermaidView = (ImageView)workspace.getViews().getViewWithKey("mermaid");
        assertEquals("diagram.mmd", mermaidView.getTitle());
        assertEquals("http://localhost:8888/img/Zmxvd2NoYXJ0IFRECiAgICBTdGFydCAtLT4gU3RvcA==?type=png", mermaidView.getContent());
        assertEquals("image/png", mermaidView.getContentType());

        ImageView krokiView = (ImageView)workspace.getViews().getViewWithKey("kroki");
        assertEquals("diagram.dot", krokiView.getTitle());
        assertEquals("http://localhost:9999/graphviz/png/eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", krokiView.getContent());
        assertEquals("image/png", krokiView.getContentType());

        ImageView pngView = (ImageView)workspace.getViews().getViewWithKey("png");
        assertEquals("image.png", pngView.getTitle());
        assertEquals("", pngView.getContent());
        assertEquals("image/png", pngView.getContentType());

        ImageView svgView = (ImageView)workspace.getViews().getViewWithKey("svg");
        assertEquals("image.svg", svgView.getTitle());
        assertEquals("", svgView.getContent());
        assertEquals("image/svg+xml", svgView.getContentType());
    }

    @Test
    void test_imageViews_ViaUrls() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/image-views/workspace-via-url.dsl"));

        Workspace workspace = parser.getWorkspace();
        assertEquals(5, workspace.getViews().getImageViews().size());

        ImageView plantumlView = (ImageView)workspace.getViews().getViewWithKey("plantuml");
        assertEquals("diagram.puml", plantumlView.getTitle());
        assertEquals("http://localhost:7777/svg/SoWkIImgAStDuNBAJrBGjLDmpCbCJbMmKiX8pSd9vt98pKi1IW80", plantumlView.getContent());
        assertEquals("image/svg+xml", plantumlView.getContentType());

        ImageView mermaidView = (ImageView)workspace.getViews().getViewWithKey("mermaid");
        assertEquals("diagram.mmd", mermaidView.getTitle());
        assertEquals("http://localhost:8888/svg/Zmxvd2NoYXJ0IFRECiAgICBTdGFydCAtLT4gU3RvcA==", mermaidView.getContent());
        assertEquals("image/svg+xml", mermaidView.getContentType());

        ImageView krokiView = (ImageView)workspace.getViews().getViewWithKey("kroki");
        assertEquals("diagram.dot", krokiView.getTitle());
        assertEquals("http://localhost:9999/graphviz/svg/eNpLyUwvSizIUHBXqPZIzcnJ17ULzy_KSanlAgB1EAjQ", krokiView.getContent());
        assertEquals("image/svg+xml", krokiView.getContentType());

        ImageView pngView = (ImageView)workspace.getViews().getViewWithKey("png");
        assertEquals("image.png", pngView.getTitle());
        assertEquals("https://raw.githubusercontent.com/structurizr/java/master/structurizr-dsl/src/test/resources/dsl/image-views/image.png", pngView.getContent());
        assertEquals("image/png", pngView.getContentType());

        ImageView svgView = (ImageView)workspace.getViews().getViewWithKey("svg");
        assertEquals("image.svg", svgView.getTitle());
        assertEquals("https://raw.githubusercontent.com/structurizr/java/master/structurizr-dsl/src/test/resources/dsl/image-views/image.svg", svgView.getContent());
        assertEquals("image/svg+xml", svgView.getContentType());
    }

    @Test
    void test_EmptyDeploymentEnvironment() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/deployment-environment-empty.dsl"));

        assertEquals(1, parser.getWorkspace().getModel().getDeploymentNodes().size());
    }

    @Test
    void test_MultiLineSupport() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/multi-line.dsl"));

        assertNotNull(parser.getWorkspace().getModel().getSoftwareSystemWithName("Software System"));
    }

    @Test
    void test_MultiLineWithError() {
        File dslFile = new File("src/test/resources/dsl/multi-line-with-error.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            // check that the error message includes the original line number
            assertEquals("Unexpected tokens (expected: !docs, !decisions, group, container, description, tags, url, properties, perspectives, ->) at line 8 of " + dslFile.getAbsolutePath() + ": component \"Component\" // components not permitted inside software systems", e.getMessage());
        }
    }

    @Test
    void test_RelationshipAlreadyExists() {
        File dslFile = new File("src/test/resources/dsl/relationship-already-exists.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("A relationship between \"SoftwareSystem://B\" and \"SoftwareSystem://A\" already exists at line 10 of " + dslFile.getAbsolutePath() + ": b -> a", e.getMessage());
        }
    }

    @Test
    void test_ExcludeImpliedRelationship() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/exclude-implied-relationship.dsl"));

        // check the system landscape view doesn't include any relationships
        assertEquals(0, parser.getWorkspace().getViews().getSystemLandscapeViews().iterator().next().getRelationships().size());
    }

    @Test
    void test_IncludeImpliedRelationship() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(new File("src/test/resources/dsl/include-implied-relationship.dsl"));

        // check the system landscape view includes a relationship
        assertEquals(1, parser.getWorkspace().getViews().getSystemLandscapeViews().iterator().next().getRelationships().size());
    }

    @Test
    void test_GroupWithoutBrace() throws Exception {
        File dslFile = new File("src/test/resources/dsl/group-without-brace.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Expected: group <name> { at line 4 of " + dslFile.getAbsolutePath() + ": group \"Name\"", e.getMessage());
        }
    }

    @Test
    void test_ISO8859Encoding() throws Exception {
        StructurizrDslParser parser = new StructurizrDslParser();
        parser.setCharacterEncoding(StandardCharsets.ISO_8859_1);
        parser.parse(new File("src/test/resources/dsl/iso-8859.dsl"));
        assertNotNull(parser.getWorkspace().getModel().getSoftwareSystemWithName("Namé"));
    }

    @Test
    void test_ScriptInDynamicView() throws Exception {
        File dslFile = new File("src/test/resources/dsl/script-in-dynamic-view.dsl");

        StructurizrDslParser parser = new StructurizrDslParser();
        parser.parse(dslFile);
    }

    @Test
    void test_Enterprise() {
        File dslFile = new File("src/test/resources/dsl/enterprise.dsl");

        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse(dslFile);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("The enterprise keyword was previously deprecated, and has now been removed - please use group instead (https://docs.structurizr.com/dsl/language#group) at line 4 of " + dslFile.getAbsolutePath() + ": enterprise \"Name\" {", e.getMessage());
        }
    }

    @Test
    void test_UnbalancedCurlyBraces() {
        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse("""
                    workspace {
                        model {
                            person "User"
                        }
                    """);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("Unexpected end of DSL content - are one or more closing curly braces missing?", e.getMessage());
        }
    }

    @Test
    void test_Const() {
        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse("""
                    workspace {
                        !const name value1
                        !const name value2
                    }
                    """);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("A constant/variable \"name\" already exists at line 3: !const name value2", e.getMessage());
        }
    }

    @Test
    void test_Var_CannotOverrideConst() {
        try {
            StructurizrDslParser parser = new StructurizrDslParser();
            parser.parse("""
                    workspace {
                        !const name value1
                        !var name value2
                    }
                    """);
            fail();
        } catch (StructurizrDslParserException e) {
            assertEquals("A constant \"name\" already exists at line 3: !var name value2", e.getMessage());
        }
    }

}